package com.xiao.mall.foundation.listener;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import com.xiao.mall.foundation.dto.EntityEventData;
import com.xiao.mall.foundation.annotation.NotAuditLog;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.*;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.persister.entity.EntityPersister;
//import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
import java.lang.annotation.Annotation;
@Slf4j
@Component
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "audit", name = "log", havingValue = "enable")
public class AuditLogEventListener implements PostInsertEventListener, PostUpdateEventListener, PostDeleteEventListener {

    private ObjectMapper objectMapper = new ObjectMapper();

    private final EntityManagerFactory entityManagerFactory;
    //private final RabbitTemplate rabbitTemplate;
    private final JavaTimeModule javaTimeModule;

    public static final String INSERT = "Insert";
    public static final String UPDATE = "Update";
    public static final String DELETE = "Delete";

    @PostConstruct
    private void init() {
        SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
        EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
        registry.getEventListenerGroup(EventType.POST_INSERT).appendListener(this);
        registry.getEventListenerGroup(EventType.POST_UPDATE).appendListener(this);
        registry.getEventListenerGroup(EventType.POST_DELETE).appendListener(this);

        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        objectMapper.disable(SerializationFeature.FAIL_ON_SELF_REFERENCES);
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.registerModule(javaTimeModule);
    }

    @Override
    public void onPostDelete(PostDeleteEvent postDeleteEvent) {

        if (disableAuditLog(postDeleteEvent.getEntity())) {
            return;
        }

        String[] propertyNames = postDeleteEvent.getPersister().getPropertyNames();
        Object[] states = postDeleteEvent.getDeletedState();

        EntityEventData entityEventData = EntityEventData.builder().operationMethod(DELETE).entity(postDeleteEvent.getEntity())
                .propertyNames(propertyNames).oldStates(states).id(postDeleteEvent.getId()).className(postDeleteEvent.getEntity().getClass().getSimpleName())
                .classFullName(postDeleteEvent.getEntity().getClass().getName())
                .build();
        try {
            final String event = objectMapper.writeValueAsString(entityEventData);
//            rabbitTemplate.convertAndSend(RabbitMqConstant.EXCHANGE_FANOUT_AUDIT_LOG_TRIGGERED, null, event);
        } catch (JsonProcessingException e) {
            log.error("Json处理异常:{}", e);
        }
    }

    @Override
    public void onPostInsert(PostInsertEvent postInsertEvent) {

        if (disableAuditLog(postInsertEvent.getEntity())) {
            return;
        }

        String[] propertyNames = postInsertEvent.getPersister().getPropertyNames();
        Object[] states = postInsertEvent.getState();
        EntityEventData entityEventData = EntityEventData.builder().operationMethod(INSERT).entity(postInsertEvent.getEntity())
                .className(postInsertEvent.getEntity().getClass().getSimpleName()).classFullName(postInsertEvent.getEntity().getClass().getName())
                .propertyNames(propertyNames).states(states).id(postInsertEvent.getId()).build();

        try {
            final String event = objectMapper.writeValueAsString(entityEventData);
//            rabbitTemplate.convertAndSend(RabbitMqConstant.EXCHANGE_FANOUT_AUDIT_LOG_TRIGGERED, null, event);
        } catch (JsonProcessingException e) {
            log.error("Json处理异常:{}", e);
        }
    }

    @SneakyThrows
    @Override
    public void onPostUpdate(PostUpdateEvent postUpdateEvent) {

        if (disableAuditLog(postUpdateEvent.getEntity())) {
            return;
        }

        String[] propertyNames = postUpdateEvent.getPersister().getPropertyNames();
        Object[] states = postUpdateEvent.getState();
        Object[] oldStates = postUpdateEvent.getOldState();
        int[] changeIndexs = postUpdateEvent.getDirtyProperties();
        EntityEventData entityEventData = EntityEventData.builder().operationMethod(UPDATE).entity(postUpdateEvent.getEntity())
                .className(postUpdateEvent.getEntity().getClass().getSimpleName()).classFullName(postUpdateEvent.getEntity().getClass().getName())
                .propertyNames(propertyNames).states(states).oldStates(oldStates).changeIndexs(changeIndexs)
                .id(postUpdateEvent.getId()).build();

        try {
            final String event = objectMapper.writeValueAsString(entityEventData);
//            rabbitTemplate.convertAndSend(RabbitMqConstant.EXCHANGE_FANOUT_AUDIT_LOG_TRIGGERED, null, event);
        } catch (JsonProcessingException e) {
            log.error("Json处理异常:{}", e);
        }
    }

    @Override
    public boolean requiresPostCommitHanding(EntityPersister entityPersister) {
        return false;
    }

    @Override
    public boolean requiresPostCommitHandling(EntityPersister persister) {
        return PostInsertEventListener.super.requiresPostCommitHandling(persister);
    }

    private boolean disableAuditLog(Object entity) {
        Annotation annotation = AnnotationUtils.findAnnotation(entity.getClass(),
                NotAuditLog.class);
        if (annotation != null) {
            return true;
        }
        return false;
    }
}
